<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      #app {
        width: 600px;
        height: 600px;
        background-color: pink;
      }

      .father {
        width: 300px;
        height: 300px;
        background-color: green;
      }
    </style>
  </head>
  <body>
    <!-- 
      依赖注入
        主要就是 provide 与 inject 两个选项的使用

      provide 语法
        一、对象语法：
          provide: {
            key1: value1,
            key2: value2,
            fn1: function() {}
          }

        二、函数返回对象语法：(推荐，这里可以获取到 this)
          provide() {
            return {
              key1: 'value1',
              key2: 'value2',
              fn1: function() {}
            }
          }
        
      inject 语法
        一、数组语法：
          inject: ['key1', 'key2']
            key1 与 key2 可以直接通过 this 获取得到，
        二、对象语法：
          inject: {
            key1: "value1",
            key2: {
              form: 'value2',
              default: ''
            }
          }
            key1 - 是当前组件中绑定到 this 实例上的属性，它的值有 value1 决定
            value1 - 是上层组件 provide 提供下来的一个key

      provide 提供的数据是没有响应式的。
          然后有时，我们在后代组件中希望能够获取到响应式的数据，这时可以：
              1. provide 某个属性 直接提供一个 this 下去
              2. provide 某个属性 直接提供一个 函数下去，函数内返回响应的值

     -->

    <div id="app">
      <button @click="myName = myName + '~'">{{ myName }}</button>
      <father></father>
    </div>

    <script src="../lib/vue.js"></script>
    <script>
      Vue.component("father", {
        template: `
          <div class="father">
            <h2>Father - {{ abc }} - {{ efg }} - {{ dtl }}</h2>  

            <hr />

            <child />
          </div>
        `,

        // inject: ["key1"],
        inject: {
          abc: "key1",
          efg: {
            from: "key2", // 来自 provide 中某个属性
          },
          dtl: {
            from: "key3",
            default: "默认值",
          },
        },
        provide: {
          key1: "father - key1",
        },
      });

      Vue.component("child", {
        template: `
          <div class="child">
            <h3>Child</h3>  
            <p>key1: {{ key1 }}</p>
            <p>key2: {{ key2 }}</p>
            <p>rootName: {{ rootName }}</p>
            <p>getRootName: {{ getRootName() }}</p>
            <p>root: {{ root.myName }}</p>
            <button @click="fn1">点我，调用 fn1</button>
          </div>
        `,
        inject: ["key1", "key2", "fn1", "rootName", "getRootName", "root"],
      });

      const vm = new Vue({
        el: "#app",

        data: {
          myName: "张三",
        },

        // provide: {
        //   key1: "abc",
        //   key2: "efg",
        //   rootName: this.myName,
        //   fn1: function () {
        //     console.log("Root Fn1");
        //   },
        // },

        provide() {
          console.log("provide this", this);

          return {
            key1: "abc",
            key2: "efg",
            key3: "哈哈哈哈",
            rootName: this.myName,
            root: this,
            // getRootName() {
            //   console.log("getRootName", this);
            //   return this.myName;
            // },
            getRootName: () => this.myName,
            fn1: function () {
              console.log("Root Fn1");
            },
          };
        },
      });
    </script>

    <script>
      function MyVue(options) {
        this.$abcd = "abcd";
        this.$provide = options.provide;
        if (typeof options.provide === "function") {
          this.$provide = options.provide.apply(this);
        }
      }

      const aaa = new MyVue({
        // provide: {
        //   rootName: this,
        // },
        provide() {
          return {
            rootName: this,
          };
        },
      });

      console.log(aaa);
    </script>
  </body>
</html>
